//==============================================================================
//
//         DFraction - the fraction number class in the ofc-library
//
//               Copyright (C) 2003-2005  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2006-01-24 19:50:45 $ $Revision: 1.4 $
//
//==============================================================================

#include "ofc/DFraction.h"

#include <stdlib.h>
#include <ctype.h>


#if _INTERFACE_

#include "ofc/config.h"

#include "ofc/DTextable.h"
#include "ofc/DParsable.h"
#include "ofc/DSimpleArithmetic.h"
#include "ofc/DComparable.h"

#include "ofc/DText.h"

//
// The fraction class implements a number of methods for working with fractions.
//

@interface DFraction : Object <DTextable,DSimpleArithmetic,DComparable,DParsable>
{
@private
  int          _num;   // the numerator
  int          _denom; // the denominator
}

#endif




@implementation DFraction


//// Constructors

//
// Initialise a zero fraction
//
// @return the object
//
- (DFraction *) init
{
  [super init];

  _num   = 0;
  _denom = 1;
  
  return self;
}

//
// Initialise a fraction
//
// @param num      the numerator
// @param denom    the denominator
//
// @return the object
//
- (DFraction *) init :(int) num :(int) denom
{
  [self init];

  [self set :num :denom];
  
  return self;
}



//// Member methods

//
// Get the numerator
//
// @return the numerator
//
- (int) numerator
{
  return _num;
}

//
// Set the numerator
//
// @param num      the numerator of the fraction
//
// @return the object
//
- (DFraction *) numerator :(int) num
{
  _num = num;

  [self norm];
  
  return self;
}

//
// Get the denominator
//
// @return the denominator
//
- (int) denominator
{
  return _denom;
}

//
// Set the denominator of the fraction
//
// @param denom    the denominator
//
// @return the object
//
- (DFraction *) denominator :(int) denom
{
  if (denom != 0)
  {
    _denom = denom;
    
    [self norm];
  }
  else
  {
    WARNING(DW_INVALID_ARG, "denom");
  }

  
  return self;
}

//
// Set the numerator and denominator of the fraction
//
// @param num      the numerator
// @param denom    the denominator
//
// @return the object
//
- (DFraction *) set :(int) num :(int) denom
{
  if (denom != 0)
  {
    _num   = num;
    _denom = denom;

    [self norm];
  }
  else
  {
    WARNING(DW_INVALID_ARG, "denom");
  }
  
  return self;
}

//
//
// Move an other object in this object
//
// @param other    the other object
//
// @return this object
//
- (DFraction *) move :(DFraction *) other
{
  _num   = [other numerator  ];
  _denom = [other denominator];

  return self;
}


//// Methods on fractions

//
// Add with an other fraction
//
// @param other    the fraction
//
// @return the object
//
- (DFraction *) add :(DFraction *) other
{
  int num   = [other numerator];
  int denom = [other denominator];
  
  if (denom == _denom)
  {
    _num += num;
  }
  else
  { 
    _num   = _num * denom + num * _denom;
    _denom = denom * _denom;
  }
  
  [self norm];
  
  return self;
}

//
// Add two fractions and store the result in the object
//
// @param fr1     the first fraction
// @param fr2     the second fraction
//
// @return the object
//
- (DFraction *) add :(DFraction *) fr1 :(DFraction *) fr2
{
  if ((fr1 == nil) || (fr2 == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "fr1/fr2");
  }
  else
  {
    if (fr1->_denom == fr2->_denom)
    {
      _num   = fr1->_num + fr2->_num;
      _denom = fr1->_denom;
    }
    else
    { 
      _num   = fr1->_num * fr2->_denom + fr2->_num * fr1->_denom;
      
      _denom = fr1->_denom * fr2->_denom;
    }
  
    [self norm];
  }
  
  return self;
}

//
// Subract with an other fraction
//
// @param other     the fraction
//
// @return the object
//
- (DFraction *) sub :(DFraction *) other
{
  int num   = [other numerator  ];
  int denom = [other denominator];
  
  if (denom == _denom)
  {
    _num -= num;
  }
  else
  { 
    _num   = _num * denom - num * _denom;
    _denom = denom * _denom;
  }
  
  [self norm];
  
  return self;
}

//
// Subtract two fractions and store the result in the object
//
// @param fr1     the first fraction
// @param fr2     the second fraction
//
// @return the object
//
- (DFraction *) sub :(DFraction *) fr1 :(DFraction *) fr2
{
  if ((fr1 == nil) || (fr2 == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "fr1/fr2");
  }
  else
  {
    if (fr1->_denom == fr2->_denom)
    {
      _num   = fr1->_num - fr2->_num;
      _denom = fr1->_denom;
    }
    else
    { 
      _num   = fr1->_num * fr2->_denom - fr2->_num * fr1->_denom;
      
      _denom = fr1->_denom * fr2->_denom;
    }
  
    [self norm];
  }
  
  return self;
}

//
// Multiply with an other fraction
//
// @param other    the other fraction
//
// @return the object
//
- (DFraction *) mul :(DFraction *) other
{ 
  _num   *= [other numerator  ];
  _denom *= [other denominator];
  
  [self norm];

  return self;
}

//
// Multiply two fractions and store the result in the object
//
// @param fr1     the first fraction
// @param fr2     the second fraction
//
// @return the object
//
- (DFraction *) mul :(DFraction *) fr1 :(DFraction *) fr2
{
  if ((fr1 == nil) || (fr2 == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "fr1/fr2");
  }
  else
  {
    _num   = fr1->_num   * fr2->_num;
    _denom = fr1->_denom * fr2->_denom;
    
    [self norm];
  }
  
  return self;
}

//
// Divide with an other fraction
//
// @param other    the divider
//
// @return the object
//
- (DFraction *) div :(DFraction *) other
{
  int num = [other numerator];
  
  if (num != 0)
  {
    _num   *= [other denominator];
    _denom *= num;
  }
  else
  {
    WARNING(DW_UNEXPECTED_ERROR, "division by zero");
  }
  
  [self norm];

  return self; 
}

//
// Divide two fractions and store the result in the object
//
// @param fraction the fraction
// @param divider  the divider
//
// @return the object
//
- (DFraction *) div :(DFraction *) fraction :(DFraction *) divider
{
  if ((fraction == nil) || (divider == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "fraction/divider");
  }
  else
  {
    if (divider->_num != 0)
    {
      _num   = fraction->_num * divider->_denom;
      _denom = fraction->_denom * divider->_num;
      
      [self norm];
    }
    else
    {
      WARNING(DW_UNEXPECTED_ERROR, "division by zero");
    }
  }

  return self; 
}

//
// Normalize the fraction
//
// @return the object
//
- (DFraction *) norm
{
  int nom = [DFraction gcd :_num :_denom];
  
  // normalize
  _num   /= nom;
  _denom /= nom;

  // keep only numerator negative
  if (_denom < 0)
  {
    _num   = -_num;
    _denom = -_denom;
  }
  
  return self;
}

//
// Convert to a double
//
// @return the double
//
- (double) toDouble
{
  return (double) _num / (double) _denom;
}

//
// Invert the fraction
//
// @return the object
//
- (DFraction *) invert
{
  if (_num != 0)
  {
    int tmp = _num;
    
    _num   = _denom;
    _denom = tmp;
    
    [self norm];
  }
  else
  {
    WARNING(DW_UNEXPECTED_ERROR, "division by zero");
  }

  return self;
}

//// Class methods

//
// Calculate the Greatest Common Divider
// 
// @param a        the first number
// @param b        the second number
// 
// @return the greatest common divider
// 
+ (int) gcd :(int) a :(int) b
{
  int r = 0;
  
  a = abs(a);
  b = abs(b);
  
  if (b > a)
  {
    int t = a; a = b; b = t;
  }
  
  do
  {
    r = a % b;
    
    if (r > 0)
    {
      a = b;
      b = r;
    }
  }
  while (r > 0);
  
  return b;
}

//
// Calculate the Least Common Multiplier
// 
// @param a        the first number
// @param b        the second number
// 
// @return the least common multiplier
// 
+ (int) lcm :(int) a :(int) b
{
  return (a * b) / [DFraction gcd :a :b];
}



//// Textable protocol methods

//
// Convert to a text string 
//
// @return a (new) text string with the complex number
//
- (DText *) toText
{
  DText *str = [DText new];
  
  if (_num == 0)
  {
    [str set :"0"];
  }
  else if (_denom == 1)
  {
    [str format :"%d", _num];
  }
  else
  {
    [str format :"%d/%d", _num, _denom];
  }

  return str;
}


//// Comparable protocol method

//
// Compare with another fraction (using doubles)
// 
// @param other   the other fraction
// 
// @return the compare result (-1,0,1)
// 
- (int) compare :(DFraction *) other
{
  int result = -1;
  
  if (other == nil)
  {
    WARNING(DW_NIL_NOT_ALLOWED, "other");
  }
  else
  {
    // Both fraction are normalized ..
     
    if ((_num == other->_num) && (_denom == other->_denom))
    {
      result = 0;
    }
    else
    {
      double dbl1 = [self  toDouble];
      double dbl2 = [other toDouble];
      
      if (dbl1 < dbl2)
      {
        result = -1;
      }
      else if (dbl1 > dbl2)
      {
        result = 1;
      }
      else // not likely
      {
        result = 0;
      }
    }
  }
  
  return result;
}


//// DParsable protocol implementation

#if _PRIVATE_
//
// Check for octal number
//
// @param ch     the character
// 
// @return is it an octal number ?
//
static BOOL _isoct(char ch)
{
  return ((ch >= '0') && (ch <= '7'));
}

//
// Scan a number
//
// @param pntr     the pointer in the string
// @param output   the output string
// 
// @return the updated pointer in the string
//
static char *_scanNumber(char *pntr, int *result)
{
  int base   = 10;

  *result = 0;
  
  if (*pntr == '0') // process base indicator
  {
    pntr++;
    
    if (tolower(*pntr) == 'x')
    {
      pntr++;
      
      base = 16;
    }
    else
    {
      base = 8;
    }
  }
  else if (isdigit(*pntr))
  {
    base = 10;
  }
  
  while ( ((isxdigit(*pntr)) && (base == 16)) || // scan the number
          ((isdigit (*pntr)) && (base == 10)) ||
          ((_isoct  (*pntr)) && (base == 8 )) )
  {
    if ((*pntr >= 'A') && (*pntr <= 'F'))
    {
      (*result) = (*result * base) + (int) (*pntr - 'A' + 10);
    }
    else if ((*pntr >= 'a') && (*pntr <= 'f'))
    {
      (*result) = (*result * base) + (int) (*pntr - 'a' + 10);
    }
    else
    {
      (*result) = (*result * base) + (int) (*pntr - '0');
    }
    
    pntr++;
  }
  
  return pntr;
}
#endif

//
// Parse a string for a big fraction. Format: [space]*[+-]number/number
// and number is one of:
// decimal:     [1..9][0..9]*
// hexadecimal: 0x[0..9a..f]*
// octal:       0[0..7]*
// 
// @param cstr     the string to be parsed (moved to the first non-parsable char)
// 
// @return the result (0,ENODATA)
// 
- (int) fromString :(char **) cstr
{
  char  *pntr     = *cstr;
  char  *start    = pntr;
  int    result   = ENODATA;
  BOOL   negative = NO;
  
  while (isspace(*pntr)) // skip spaces
  {
    pntr++;
  }

  start = pntr;

  if ((*pntr == '+') || (*pntr == '-')) // process sign
  {
    negative = (*pntr == '-');
    
    pntr++;
  }
  
  pntr = _scanNumber(pntr, &_num);
  
  if (*pntr == '/')
  {
    pntr++;
    
    pntr = _scanNumber(pntr, &_denom);
  }
  else
  {
    _denom = 1;
  }
  
  if (pntr != start)
  {
    [self norm];
      
    result = 0;
      
    *cstr = pntr;
  }
  
  return result;
}

@end

/*===========================================================================*/

